package com.wlyuan.open.infrastructure.repository;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wlyuan.open.dao.entity.SourceDataDO;
import com.wlyuan.open.dao.entity.SourceRelationDO;
import com.wlyuan.open.dao.mapper.SourceDataMapper;
import com.wlyuan.open.dao.mapper.SourceRelationMapper;
import com.wlyuan.open.domain.source.*;
import com.wlyuan.open.domain.source.repository.SourceRepository;
import com.wlyuan.open.infrastructure.converter.SourceConverter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author yuanzheng
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SourceRepositoryImpl implements SourceRepository {
    private final SourceDataMapper sourceDataMapper;
    private final SourceRelationMapper relationMapper;

    private QueryWrapper<SourceRelationDO> makeRelationQuery(SourcePattern pattern) {
        QueryWrapper<SourceRelationDO> query = new QueryWrapper<>();
        query.lambda().eq(SourceRelationDO::getSourceClass, pattern.getSourceClass().getValue());
        if (StringUtils.isNotBlank(pattern.getSourceKey())) {
            query.lambda().eq(SourceRelationDO::getSourceKey, pattern.getSourceKey());
        }
        if (pattern.getTenantId() != null) {
            query.lambda().eq(SourceRelationDO::getTenantId, pattern.getTenantId().getId());
        }
        return query;
    }

    @Override
    public void save(@NonNull SourceRelation relation) {
        SourceRelationDO relationDO = SourceConverter.convertRelation(relation);
        QueryWrapper<SourceRelationDO> query = makeRelationQuery(relation.getPattern());
        if (relationMapper.selectCount(query) <= 0) {
            relationMapper.insert(relationDO);
        } else {
            relationMapper.update(relationDO, query);
        }
    }

    @Override
    public SourceRelation ofPattern(@NonNull SourcePattern pattern) {
        var query = makeRelationQuery(pattern);
        return SourceConverter.convertRelation(relationMapper.selectOne(query));
    }

    @Override
    public SourceDataId ofSourcePattern(SourcePattern pattern) {
        QueryWrapper<SourceDataDO> query = new QueryWrapper<>();
        query.select("id");
        query.lambda().eq(SourceDataDO::getSourceKey, pattern.getSourceKey()).orderByDesc(SourceDataDO::getId).last(" limit 1");
        return Optional.ofNullable(SourceConverter.convertRaw(sourceDataMapper.selectOne(query))).map(SourceData::getId).orElse(null);
    }

    @Override
    public void updateSource(SourceDataId id, Integer status, String remark) {
        SourceDataDO sourceDataDO = new SourceDataDO();
        sourceDataDO.setId(id.getId());
        sourceDataDO.setStatus(status);
        sourceDataDO.setRemark(remark);
        sourceDataMapper.updateById(sourceDataDO);
    }

    @Override
    public List<String> ofFailPattern(SourceDataPattern pattern) {
        QueryWrapper<SourceDataDO> query = new QueryWrapper<>();
        query.select("source_key");
        query.lambda().eq(SourceDataDO::getSourceClass, pattern.getSourceClass().getValue()).eq(SourceDataDO::getStatus, SourceStatus.FAILED.getValue());
        if (pattern.getTenantId() != null) {
            query.lambda().eq(SourceDataDO::getTenantId, pattern.getTenantId());
        }
        if (StringUtils.isNotBlank(pattern.getCreateAt())) {
            query.lambda().ge(SourceDataDO::getCreateAt, pattern.getCreateAt());
        }
        var list = sourceDataMapper.selectList(query);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        return list.stream().map(SourceDataDO::getSourceKey).distinct().collect(Collectors.toList());
    }

    @Override
    public SourceData ofId(@NonNull SourceDataId sourceDataId) {
        return SourceConverter.convertRaw(sourceDataMapper.selectById(sourceDataId));
    }

    @Override
    public SourceDataId save(@NonNull SourceData sourceData) {
        SourceDataDO sourceDataDO = SourceConverter.convertRaw(sourceData);
        if (sourceDataDO.getId() == null) {
            sourceDataMapper.insert(sourceDataDO);
            sourceData.setId(new SourceDataId(sourceDataDO.getId()));
        } else {
            sourceDataMapper.updateById(sourceDataDO);
        }
        return sourceData.getId();
    }

    @Override
    public void remove(@NonNull SourceDataId sourceDataId) {
        sourceDataMapper.deleteById(sourceDataId.getId());
    }

    @Override
    public List<SourceData> list(@NonNull Collection<SourceDataId> sourceDataIds) {
        return sourceDataIds.stream()
                .map(this::ofId)
                .collect(Collectors.toList());
    }
}
